package main

import "fmt"
import "github.com/pushdotccgzs/coapmsg"

const LATEST_PEER_ADDR string = "LATEST_PEER_ADDR"
const LATEST_DEVICE_TOKEN string = "LATEST_DEVICE_TOKEN"
const MESSAGE_DIRECTION_DOWN uint = 0
const MESSAGE_DIRECTION_UP uint = 1

func QueryDeviceFromToken(token string) (device Device, err error) {
	query := gldb.Joins("JOIN coap_sessions on coap_sessions.device_id=devices.id")
	query = query.Where("coap_sessions.token=?", token).First(&device)
	if query.Error != nil {
		logger.Warning("query imei from token failed.", query.Error)
		err = query.Error
		return
	}
	return
}

func InsertNewCoAPSession(token string, devdbid uint) (err error) {
	coapsession := CoapSession{
		DeviceID: int(devdbid),
		Token:    token,
		Stat:     "VALID",
	}
	query := gldb.Create(&coapsession)
	if query.Error != nil {
		err = query.Error
		return
	}
	return
}

func QueryDeviceID(imei string) (device Device, err error) {
	query := gldb.Where("stat='VALID' and chip_id=?", imei).First(&device)
	if query.Error != nil {
		err = query.Error
		return
	}
	return
}

func QueryPushMessageList(devdbid uint) (msgs []CoapMessage, err error) {
	query := gldb.Where("stat='PLANING' and device_id=?", devdbid).Find(&msgs)
	if query == nil {
		logger.Warning("QueryPushMessageList failed....")
		err = fmt.Errorf("db query fail, get query nil.")
		return
	}
	if query.Error != nil {
		err = query.Error
		return
	}
	return
}

func DbDeviceRegister(imei string, uuid string) (devdbid int, err error) {
	assert(imei != "")
	device := Device{
		Name:    "",
		ChipId:  imei,
		DevUuid: uuid,
		Stat:    "VALID",
	}
	gldb.Create(&device)
	devdbid = int(device.ID)
	return
}

func DbMessageRemove(msgid uint) (err error) {
	var msg CoapMessage

	query := gldb.First(&msg, msgid)
	if query.Error != nil {
		logger.Warningf("cannot found msg %d", msgid)
		err = query.Error
		return
	}

	msg.Stat = "DELETED"
	gldb.Save(&msg)
	gldb.Delete(&msg)
	return
}

/*
	DeviceID  int
	Msgid     uint
	Direction uint
	Token     string `gorm:"size:16"`
	Data      string `gorm:"type:text"`
	Stat      string `gorm:"size:16"`
*/
func DbNewPushMessage(devdbid int, data []byte, stat string) (msgid int, err error) {
	msg := CoapMessage{
		DeviceID:  devdbid,
		Direction: MESSAGE_DIRECTION_DOWN,
		Data:      string(data),
		Stat:      stat,
	}

	result := gldb.Create(&msg)
	if result.Error != nil {
		logger.Warningf("create new push messages failed. %s", result.Error)
		err = result.Error
		return
	}
	msgid = int(msg.ID)
	return
}

func DbDeviceUnRegister(devdbid uint) (err error) {
	var dev Device
	//TODO error.
	gldb.First(&dev, devdbid)
	dev.Stat = "DELETED"
	gldb.Save(&dev)
	gldb.Delete(&dev)
	return
}

func DbChangeMsgStat(msgdbid uint, stat string) (err error) {
	var msg CoapMessage
	//TODO error.
	gldb.First(&msg, msgdbid)
	msg.Stat = stat
	gldb.Save(&msg)
	return
}

func DbDeviceDetail(devdbid uint) (devinfo DeviceInfo, err error) {
	var dev Device

	query := gldb.First(&dev, devdbid)
	if query.Error != nil {
		logger.Warningf("query %d error. %s", devdbid, query.Error)
		err = query.Error
		return
	}
	devinfo.Devdbid = int(dev.ID)
	devinfo.DevUUID = dev.DevUuid
	devinfo.Imei = dev.ChipId
	devinfo.CreateTime = TimeToString(dev.CreatedAt)
	devinfo.Stat = dev.Stat

	//TODO error
	var devattr DeviceAttr
	gldb.Where("device_id=? and key=?", dev.ID, LATEST_PEER_ADDR).Find(&devattr)
	devinfo.PeerAddr = devattr.Value
	return
}

func DbDeviceQuery(imei string) (devinfo DeviceInfo, err error) {
	device, err := QueryDeviceID(imei)
	if err != nil {
		logger.Warningf("query device %s failed. %s", imei, err)
		return
	}
	return DbDeviceDetail(device.ID)
}

/*
	DeviceID  int
	Msgid     uint
	Direction uint
	Token     string `gorm:"size:16"`
	Data      string `gorm:"type:text"`
	Stat      string `gorm:"size:16"`
*/
func DbAddUploadMsg(message coapmsg.Message, addr string, devdbid int) (err error) {
	msgid := message.GetMessageId()
	token := string(message.GetToken())
	data := message.GetPayload().GetBytes()
	msg := CoapMessage{
		DeviceID:  devdbid,
		Direction: MESSAGE_DIRECTION_UP,
		Msgid:     uint(msgid),
		Token:     token,
		Data:      string(data),
		Stat:      "UPLOADED",
	}
	//TODO
	gldb.Create(&msg)
	return
}

func DbUpdateDevAddr(devdbid int, addr string) (err error) {
	var devattr DeviceAttr
	//TODO error.
	gldb.FirstOrCreate(&devattr, DeviceAttr{
		DeviceID: devdbid,
		Key:      LATEST_PEER_ADDR,
	})
	devattr.Value = addr
	gldb.Save(&devattr)
	return
}

func DbFetchDevLastToken(devdbid int) (token string, err error) {
	var devattrs []DeviceAttr
	query := gldb.Where("device_id=? and key=?", devdbid, LATEST_DEVICE_TOKEN).Find(&devattrs)
	if query.Error != nil {
		err = query.Error
		logger.Warning("query device last token failed.")
		return
	}
	if len(devattrs) == 0 {
		logger.Warning("query device last token empty.")
		err = fmt.Errorf("query latest token device empty.")
		return
	}

	token = devattrs[0].Value
	return
}

func DbUpdateDevLastToken(devdbid int, token string) (err error) {
	var devattr DeviceAttr
	gldb.FirstOrCreate(&devattr, DeviceAttr{
		DeviceID: devdbid,
		Key:      LATEST_DEVICE_TOKEN,
	})
	devattr.Value = token
	gldb.Save(&devattr)
	return
}

func QueryMessageFromMsgID(msgdbid uint) (msg CoapMessage, err error) {
	err = gldb.Where("id=?", msgdbid).First(&msg).Error
	if err != nil {
		logger.Warningf("query device from msgid %d failed.", msgdbid)
		return
	}
	return
}

func QueryDeviceFromID(devdbid uint) (device Device, err error) {
	err = gldb.Where("id=? and stat='VALID'", devdbid).First(&device).Error
	return
}

func QueryUserFromEmailToken(email string, token string) (user User, err error) {
	query := gldb.Joins("join user_auth_tokens on user_auth_tokens.user_id = users.id")
	query = query.Where("users.email=? and user_auth_tokens.token=? and users.stat='VALID' and user_auth_tokens.stat='VALID'", email, token)
	query = query.First(&user)
	if query.Error != nil {
		logger.Warningf("query user token failed. %s", query.Error)
		err = query.Error
		return
	}
	return
}

func QueryUserIDFromAppID(appdbid uint) (userdbid uint, err error) {
	var app App
	err = gldb.Where("id=? and stat='VALID'", appdbid).First(&app).Error
	userdbid = app.UserID
	return
}
